////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "Test_SteeringGameImpl.hpp"

#include <sstream>

#include <SFML/Graphics.hpp>

#include <SmegCore/Game/Game.hpp>
#include <SmegCore/Game/DataManager.hpp>
#include <SmegCore/Entity/EntityManager.hpp>

#include "Obstacle.hpp"
#include "Wall.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

////////////////////////////////////////////////////////////
/// Defines
////////////////////////////////////////////////////////////
#define BASE_DIST_TO_WAYPOINT  30.0f
#define START_ENTITIES  10
#define MIN_ENTITIES    0
#define MAX_ENTITIES    10000

#define START_OBSTACLES 25
#define MIN_OBSTACLES   0
#define MAX_OBSTACLES   100

////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
Test_SteeringGameImpl::Test_SteeringGameImpl() :
    DefaultGameImpl(),
    m_pFont( NULL ),
    m_pInfo( NULL ),
    m_bShowDebug(false),
    m_pMouseEntity( NULL ),
    m_eCurrentComputeMethod(smeg::ai::SteeringManager::ePrioritized),
    m_eCurrentBehavior(eSeek),
    m_fBehaviorWeight(1.0f),
    m_bIsSmoothingOn(false),
    m_bPause(false),

    // Offset Pursue
    m_OffsetPursue(),

    // Arrive
    m_iArrivalDeceleration(smeg::ai::SteeringManager::eNormal),
    m_fArriveThreshold(-1.0f),

    // Wander
    m_fWanderRadius(0.8f),
    m_fWanderDistance(2.0f),
    m_fWanderJitter(50.0f),

    // Path following
    m_fDistToWaypoint(BASE_DIST_TO_WAYPOINT),
    m_iLoopType(smeg::ai::PointPath::eNoLoop),
    m_bReversePath(false),

    m_iNumEntities(0),
    m_iAddingEntities(0),
    
    // Separation / Alignment / Cohesion / Obstacle Avoidance
    m_fViewRadius( 100.0f ),

    // Flocking
    m_fSeparationWeight( 500.0f ),
    m_fAlignmentWeight( 10.0f ),
    m_fCohesionWeight( 1.0f ),
    
    // Wall Avoidance
    m_fFeelerLength( 40.0f ),

    // Obstacle Avoidance
    m_fBrakingWeight( 0.2f )
{
}

////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
Test_SteeringGameImpl::~Test_SteeringGameImpl()
{
}

////////////////////////////////////////////////////////////
/// Init the game
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::Init( sf::RenderWindow* _pRenderWindow ) {
    DefaultGameImpl::Init( _pRenderWindow );
    m_pRenderWindow->setMouseCursorVisible( false );
    
    // Info
    m_pFont = new sf::Font();
    if ( m_pFont->loadFromFile( "data/fonts/COUR.ttf" ) ) {
        m_pInfo = new sf::Text("FPS : 0\n",
                                *m_pFont,
                                12);
        m_pInfo->setColor( sf::Color::White );
    }
    
    // Tex
    smeg::core::DataManager::GetInstance()->AddTexture( "Mouse", "data/mouse.png" );
    smeg::core::DataManager::GetInstance()->AddTexture( "LadyBug", "data/Ladybug.png" );

    // Path
    m_PathToFollow.AddWaypoint( sf::Vector2f( 160.0f, 280.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 300.0f, 100.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 350.0f, 250.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 500.0f, 160.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 520.0f, 460.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 630.0f, 300.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 850.0f, 160.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 670.0f, 580.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 830.0f, 700.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 430.0f, 740.0f ) );
    m_PathToFollow.AddWaypoint( sf::Vector2f( 30.0f, 650.0f ) );

    m_PathToFollow.SetLoopType( (smeg::ai::PointPath::eLoopType)m_iLoopType );
    m_PathToFollow.Reverse( m_bReversePath );

    // Obstacles
    m_iAddingObstacles = START_OBSTACLES;
    AddObstacles();
    m_iAddingObstacles = START_OBSTACLES;
    AddWalls();

    // Entities
    CreateMouse();

    m_iAddingEntities = START_ENTITIES;
    AddEntities();
    ChangeBehavior( m_eCurrentBehavior );
}

////////////////////////////////////////////////////////////
/// DeInit the game
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::DeInit() {
    delete m_pFont;
    m_pFont = NULL;
    delete m_pInfo;
    m_pInfo = NULL;

    DefaultGameImpl::DeInit();
}

////////////////////////////////////////////////////////////
/// Manage an incoming event
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::ManageEvent( const sf::Event& _Event ) {
    DefaultGameImpl::ManageEvent( _Event );

    switch ( _Event.type ) {
        case sf::Event::KeyPressed: {
            bool bValueChanged = false;
            for ( int i = 0; i < MAX_VALUES_TO_CHANGE; ++i ) {
                if ( m_ValuesToChange[i].Compute( _Event.key.code ) ) {
                    bValueChanged = true;
                }
            }

            if ( bValueChanged ) {
                ChangeBehavior( m_eCurrentBehavior );
            }

            switch( _Event.key.code ) {
            case sf::Keyboard::F1:
                m_bShowDebug = !m_bShowDebug;
                break;
            case sf::Keyboard::F2:
                m_bPause = !m_bPause;
                break;

            // Change compute method
            case sf::Keyboard::PageUp:
                ChangeComputeMethod( m_eCurrentComputeMethod + 1 );
                break;
            case sf::Keyboard::PageDown:
                ChangeComputeMethod( m_eCurrentComputeMethod - 1 );
                break;

            // Change behaviors
            case sf::Keyboard::Up:
                ChangeBehavior( m_eCurrentBehavior + 1 );
                break;
            case sf::Keyboard::Down:
                ChangeBehavior( m_eCurrentBehavior - 1 );
                break;

            // Add entities
            case sf::Keyboard::Add:
                if ( m_eCurrentBehavior != eInterpose ) {
                    if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LControl ) && sf::Keyboard::isKeyPressed( sf::Keyboard::LShift ) ) {
                        m_iAddingEntities += 1000;
                    } else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LShift ) ) {
                        m_iAddingEntities += 100;
                    } else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LControl ) ) {
                        m_iAddingEntities += 10;
                    } else {
                        ++m_iAddingEntities;
                    }
                    if ( m_iNumEntities + m_iAddingEntities > MAX_ENTITIES ) {
                        m_iAddingEntities = MAX_ENTITIES - m_iNumEntities;
                    }
                } else {
                    m_iAddingEntities = 0;
                }
                break;
            case sf::Keyboard::Subtract:
                if ( m_eCurrentBehavior != eInterpose ) {
                    if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LControl ) && sf::Keyboard::isKeyPressed( sf::Keyboard::LShift ) ) {
                        m_iAddingEntities -= 1000;
                    } else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LShift ) ) {
                        m_iAddingEntities -= 100;
                    } else if ( sf::Keyboard::isKeyPressed( sf::Keyboard::LControl ) ) {
                        m_iAddingEntities -= 10;
                    } else {
                        --m_iAddingEntities;
                    }
                    if ( m_iNumEntities + m_iAddingEntities < MIN_ENTITIES ) {
                        m_iAddingEntities = MIN_ENTITIES - m_iNumEntities;
                    }
                } else {
                    m_iAddingEntities = 0;
                }
                break;

            // Add obstacles
            case sf::Keyboard::Home:
                ++m_iAddingObstacles;
                if ( (int)m_Obstacles.size() + m_iAddingObstacles > MAX_OBSTACLES ) {
                    m_iAddingObstacles = MAX_OBSTACLES - m_Obstacles.size();
                }
                break;
            case sf::Keyboard::End:
                --m_iAddingObstacles;
                if ( (int)m_Obstacles.size() + m_iAddingObstacles < MIN_OBSTACLES ) {
                    m_iAddingObstacles = MIN_OBSTACLES - m_Obstacles.size();
                }
                break;
            }
        }
        break;

        case sf::Event::KeyReleased:
            switch( _Event.key.code ) {
            // End add entities
            case sf::Keyboard::Add:
            case sf::Keyboard::Subtract:
                AddEntities();
                break;

            // End add obstacles
            case sf::Keyboard::Home:
            case sf::Keyboard::End:
                if ( m_eCurrentBehavior == eObstacleAvoidance ) {
                    AddObstacles();
                } else if ( m_eCurrentBehavior == eWallAvoidance ) {
                    AddWalls();
                } else {
                    m_iAddingObstacles = 0;
                }
                break;
            }
        break;
    }
}

////////////////////////////////////////////////////////////
/// Call all the Update in the other systems
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::Update( float _fDeltaTime  ) {
    // Update mouse
    sf::Vector2i mousePos = sf::Mouse::getPosition( *m_pRenderWindow );
    m_pMouseEntity->setPosition( (float)mousePos.x, (float)mousePos.y );

    if ( !m_bPause ) {
        DefaultGameImpl::Update( _fDeltaTime );
    }
    
    std::stringstream infoStr;
    infoStr << "FPS : " << (1.0f / smeg::core::Game::GetInstance()->GetElapsedTime().asSeconds()) << std::endl;
    infoStr << "Pause (F1) : " << (m_bPause ? "ON" : "OFF") << std::endl;
    infoStr << "Num entities (+/-) : " << m_iNumEntities;
    if ( m_iAddingEntities != 0 ) {
        infoStr << " (Adding : " << m_iAddingEntities << ")" << std::endl;
    } else {
        infoStr << std::endl;
    }
    if ( m_eCurrentBehavior == eObstacleAvoidance || m_eCurrentBehavior == eWallAvoidance ) {
        infoStr << "Num obstacles (Home/End) : " << ( m_eCurrentBehavior == eObstacleAvoidance ? m_Obstacles.size() : m_Walls.size() );
        if ( m_iAddingObstacles != 0 ) {
            infoStr << " (Adding : " << m_iAddingObstacles << ")" << std::endl;
        } else {
            infoStr << std::endl;
        }
    }
    infoStr << "Compute Method (PageUp/PageDown) : " << GetComputeMethodName() << std::endl;
    infoStr << "Behavior (Up/Down) : " << GetBehaviorName() << std::endl << std::endl;

    for ( int i = 0; i < MAX_VALUES_TO_CHANGE; ++i ) {
        infoStr << m_ValuesToChange[i];
    }

    m_pInfo->setString( infoStr.str() );
}

////////////////////////////////////////////////////////////
/// Call all the Render in the other systems
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::Render() {
    DefaultGameImpl::Render();

    // Show info
    m_pRenderWindow->draw( *m_pInfo );

    if ( m_bShowDebug ) {
        DebugRender();
    }
}

////////////////////////////////////////////////////////////
/// Behaviors
////////////////////////////////////////////////////////////
void Test_SteeringGameImpl::CreateMouse() {
    const sf::Texture* pTexMouse    = smeg::core::DataManager::GetInstance()->GetTexture( "Mouse" );
    m_pMouseEntity                  = new smeg::core::MovableEntity( pTexMouse );
    smeg::core::EntityManager::GetInstance()->AddEntity( m_pMouseEntity );
    m_pMouseEntity->RemoveFromQuadTree();
}

Test_SteeringEntity* Test_SteeringGameImpl::CreateEntity() {
    const sf::Texture* pTex         = smeg::core::DataManager::GetInstance()->GetTexture( "LadyBug" );
    Test_SteeringEntity* pNewEntity  = new Test_SteeringEntity( pTex );

    pNewEntity->setPosition( smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().x - 50.0f ), smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().y - 50.0f ) );
    pNewEntity->SetMaxSpeed( 50.0f );
    pNewEntity->SetMaxForce( 500.0f );
    pNewEntity->SetMass( 60.0f );
    pNewEntity->GetSteeringManager()->SetComputeMethod( m_eCurrentComputeMethod );
    pNewEntity->SetColor( sf::Color( smeg::math::RandRange( 0, 255 ), smeg::math::RandRange( 0, 255 ), smeg::math::RandRange( 0, 255 ) ) );
    pNewEntity->SetSmoothingOn( m_bIsSmoothingOn );

    smeg::core::EntityManager::GetInstance()->AddEntity( pNewEntity );
    m_Entities.push_back( pNewEntity );

    return pNewEntity;
}

void Test_SteeringGameImpl::AddEntities() {
    if ( m_iAddingEntities > 0 ) {
        for (int i = 0; i < m_iAddingEntities; ++i) {
            SetEntityBehavior( CreateEntity() );
        }
    } else {
        for (int i = m_iAddingEntities; i < 0; ++i) {
            RemoveEntity();
        }
    }

    m_iNumEntities += m_iAddingEntities;
    m_iAddingEntities = 0;
}

void Test_SteeringGameImpl::RemoveEntity() {
    if ( m_Entities.size() > 0 ) {
        smeg::core::EntityManager::GetInstance()->RemoveEntity( m_Entities.back() );
        m_Entities.pop_back();
    }
}

void Test_SteeringGameImpl::RemoveAllEntities() {
    while ( !m_Entities.empty() ) {
        RemoveEntity();
    }
}

void Test_SteeringGameImpl::ChangeComputeMethod( int _iNewComputeMethod ) {
    if ( _iNewComputeMethod < 0) {
        m_eCurrentComputeMethod = (smeg::ai::SteeringManager::eComputeMethod)(smeg::ai::SteeringManager::eMAX_COMPUTE_METHOD - 1);
    } else if ( _iNewComputeMethod >= smeg::ai::SteeringManager::eMAX_COMPUTE_METHOD ) {
        m_eCurrentComputeMethod = (smeg::ai::SteeringManager::eComputeMethod)0;
    } else {
        m_eCurrentComputeMethod = (smeg::ai::SteeringManager::eComputeMethod)_iNewComputeMethod;
    }

    for (int i = 0; i < m_iNumEntities; ++i) {
        m_Entities[i]->GetSteeringManager()->SetComputeMethod( m_eCurrentComputeMethod );
    }
}

void Test_SteeringGameImpl::ChangeBehavior( int _iNewBehavior ) {
    if ( _iNewBehavior < 0) {
        m_eCurrentBehavior = (eBehaviors)(eMAX_BEHAVIORS - 1);
    } else if ( _iNewBehavior >= eMAX_BEHAVIORS ) {
        m_eCurrentBehavior = (eBehaviors)0;
    } else {
        m_eCurrentBehavior = (eBehaviors)_iNewBehavior;
    }
    
    ResetAllValuesToChange();
    SetupValuesToChange();

    if ( m_eCurrentBehavior == eInterpose ) {
        RemoveAllEntities();

        // Create two entities that follow the mouse
        Test_SteeringEntity* pEntityMouse1 = CreateEntity();
        pEntityMouse1->GetSteeringManager()->PursueOn( m_pMouseEntity, m_fBehaviorWeight );
        pEntityMouse1->SetColor( sf::Color::White );

        Test_SteeringEntity* pEntityMouse2 = CreateEntity();
        pEntityMouse2->GetSteeringManager()->OffsetPursueOn( m_pMouseEntity, sf::Vector2f( 250.0f, 0.0f ), m_fBehaviorWeight );
        pEntityMouse2->SetColor( sf::Color::White );

        // Last entites interpose between the first two
        Test_SteeringEntity* pEntityMouseInterpose = CreateEntity();
        pEntityMouseInterpose->GetSteeringManager()->InterposeOn( pEntityMouse1, pEntityMouse2, m_fBehaviorWeight );
        pEntityMouseInterpose->SetColor( sf::Color::Green );
    } else {
        if ( m_iNumEntities != m_Entities.size() ) {
            m_iAddingEntities   = m_iNumEntities - m_Entities.size();
            m_iNumEntities      = m_Entities.size();
            AddEntities();
        }

        smeg::core::EntityManager* pEntityMgr = smeg::core::EntityManager::GetInstance();
        if ( m_eCurrentBehavior == eObstacleAvoidance ) {
            for ( unsigned i = 0; i < m_Obstacles.size(); ++i ) {
                pEntityMgr->AddEntity( m_Obstacles[i] );
            }
        } else {
            for ( unsigned i = 0; i < m_Obstacles.size(); ++i ) {
                pEntityMgr->RemoveEntity( m_Obstacles[i], false );
            }
        }
        if ( m_eCurrentBehavior == eWallAvoidance ) {
            for ( unsigned i = 0; i < m_Walls.size(); ++i ) {
                pEntityMgr->AddEntity( m_Walls[i] );
            }
        } else {
            for ( unsigned i = 0; i < m_Walls.size(); ++i ) {
                pEntityMgr->RemoveEntity( m_Walls[i], false );
            }
        }

        for (int i = 0; i < m_iNumEntities; ++i) {
            SetEntityBehavior( m_Entities[i] );
        }
    }
}

void Test_SteeringGameImpl::SetEntityBehavior( Test_SteeringEntity* _pEntity ) {
    _pEntity->GetSteeringManager()->AllOff();

    switch ( m_eCurrentBehavior ) {
    case eSeek:
        _pEntity->GetSteeringManager()->SeekOn( m_pMouseEntity, m_fBehaviorWeight );
        break;
    case eFlee:
        _pEntity->GetSteeringManager()->FleeOn( m_pMouseEntity, m_fBehaviorWeight );
        break;
    case ePursue:
        _pEntity->GetSteeringManager()->PursueOn( m_pMouseEntity, m_fBehaviorWeight );
        break;
    case eOffsetPursue:
        _pEntity->GetSteeringManager()->OffsetPursueOn( m_pMouseEntity, m_OffsetPursue, m_fBehaviorWeight );
        break;
    case eEvade:
        _pEntity->GetSteeringManager()->EvadeOn( m_pMouseEntity, m_fBehaviorWeight );
        break;
    case eArrive:
        if ( m_fArriveThreshold < 1.0f ) {
            m_fArriveThreshold = -1.0f;
        }
        _pEntity->GetSteeringManager()->ArriveOn( m_pMouseEntity, (smeg::ai::SteeringManager::eDeceleration)m_iArrivalDeceleration, m_fArriveThreshold, m_fBehaviorWeight );
        break;
    case eWander:
        _pEntity->GetSteeringManager()->WanderOn( m_fWanderRadius, m_fWanderDistance, m_fWanderJitter, m_fBehaviorWeight );
        break;
    case eInterpose:
        break;
    case ePathFollowing:
        m_PathToFollow.Reverse( m_bReversePath );
        m_PathToFollow.SetLoopType( (smeg::ai::PointPath::eLoopType)m_iLoopType );
        _pEntity->GetSteeringManager()->PathFollowingOn( m_PathToFollow, m_fDistToWaypoint, m_fBehaviorWeight );
        break;
    case eSeparation:
        _pEntity->GetSteeringManager()->SeparationOn( m_fViewRadius, m_fBehaviorWeight );
        break;
    case eAlignment:
        _pEntity->GetSteeringManager()->AlignmentOn( m_fViewRadius, m_fBehaviorWeight );
        break;
    case eCohesion:
        _pEntity->GetSteeringManager()->CohesionOn( m_fViewRadius, m_fBehaviorWeight );
        break;
    case eFlocking:
        _pEntity->GetSteeringManager()->SeekOn( m_pMouseEntity, m_fBehaviorWeight );
        _pEntity->GetSteeringManager()->SeparationOn( m_fViewRadius, m_fSeparationWeight );
        _pEntity->GetSteeringManager()->AlignmentOn( m_fViewRadius, m_fAlignmentWeight );
        _pEntity->GetSteeringManager()->CohesionOn( m_fViewRadius, m_fCohesionWeight );
        break;
    case eWallAvoidance:
        _pEntity->GetSteeringManager()->WanderOn( m_fWanderRadius, m_fWanderDistance, m_fWanderJitter, 1.0f );
        _pEntity->GetSteeringManager()->WallAvoidanceOn( m_fFeelerLength, m_fViewRadius, m_fBehaviorWeight );
        break;
    case eObstacleAvoidance:
        _pEntity->GetSteeringManager()->WanderOn( m_fWanderRadius, m_fWanderDistance, m_fWanderJitter, 1.0f );
        _pEntity->GetSteeringManager()->ObstacleAvoidanceOn( m_fBrakingWeight, m_fViewRadius, m_fBehaviorWeight );
        break;
    }
}

void Test_SteeringGameImpl::ChangeEntitySmoothing() {
    for (unsigned i = 0; i < m_Entities.size(); ++i) {
        m_Entities[i]->SetSmoothingOn( m_bIsSmoothingOn );
    }
}

void Test_SteeringGameImpl::AddObstacles() {
    Obstacle* pObstacle;
    smeg::core::EntityManager* pEntityMgr = smeg::core::EntityManager::GetInstance();
    if ( m_iAddingObstacles > 0 ) {
        for (int i = 0; i < m_iAddingObstacles; ++i) {
            pObstacle = new Obstacle();
            pObstacle->setPosition( smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().x - 50.0f ), smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().y - 50.0f ) );

            m_Obstacles.push_back( pObstacle );
        }
    } else {
        if ( abs( m_iAddingObstacles ) > (int)m_Obstacles.size() ) {
            m_iAddingObstacles = -(int)m_Obstacles.size();
        }
        for (int i = m_iAddingObstacles; i < 0; ++i) {
            pEntityMgr->RemoveEntity( m_Obstacles.back() );
            m_Obstacles.pop_back();
        }
    }

    m_iAddingObstacles = 0;
}

void Test_SteeringGameImpl::AddWalls() {
    Wall* pWall;
    smeg::core::EntityManager* pEntityMgr = smeg::core::EntityManager::GetInstance();
    if ( m_iAddingObstacles > 0 ) {
        for (int i = 0; i < m_iAddingObstacles; ++i) {
            pWall = new Wall();
            pWall->setPosition( smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().x - 50.0f ), smeg::math::RandRange( 50.0f, m_pRenderWindow->getSize().y - 50.0f ) );

            m_Walls.push_back( pWall );
        }
    } else {
        if ( abs( m_iAddingObstacles ) > (int)m_Walls.size() ) {
            m_iAddingObstacles = -(int)m_Walls.size();
        }
        for (int i = m_iAddingObstacles; i < 0; ++i) {
            pEntityMgr->RemoveEntity( m_Walls.back() );
            m_Walls.pop_back();
        }
    }

    m_iAddingObstacles = 0;
}

void Test_SteeringGameImpl::ResetAllValuesToChange() {
    for (int i = 0; i < MAX_VALUES_TO_CHANGE; ++i ) {
        m_ValuesToChange[i].Reset();
    }
}

void Test_SteeringGameImpl::SetupValuesToChange() {
    int iCurrentValue = 0;
    m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::Q, 
                                "Smoothing On",
                                &m_bIsSmoothingOn );
    ++iCurrentValue;
    m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::A, 
                                "Behavior Weight",
                                &m_fBehaviorWeight,
                                eDecrease,
                                0.5f,
                                0.5f,
                                false,
                                &m_ValuesToChange[iCurrentValue + 1] );
    ++iCurrentValue;
    m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                sf::Keyboard::Z );
    ++iCurrentValue;

    switch ( m_eCurrentBehavior ) {
    case eSeek:
        break;
    case eFlee:
        break;
    case ePursue:
        break;
    case eOffsetPursue:
        // Offset X
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "Offset X",
                                    &m_OffsetPursue.x,
                                    eDecrease,
                                    1.0f,
                                    -FLT_MAX,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;
        
        // Offset Y
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Offset Y",
                                    &m_OffsetPursue.y,
                                    eDecrease,
                                    1.0f,
                                    -FLT_MAX,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        break;
    case eEvade:
        break;
    case eArrive:
        // Deceleration type
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "Deceleration type",
                                    &m_iArrivalDeceleration,
                                    eDecrease,
                                    &Test_SteeringGameImpl::GetDecelerationTypeName,
                                    this,
                                    1,
                                    0,
                                    true,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterInt( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R,
                                    smeg::ai::SteeringManager::eMAX_DECELERATION - 1 );
        ++iCurrentValue;
        
        // Arrival threshold
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Arrival threshold",
                                    &m_fArriveThreshold,
                                    eDecrease,
                                    0.5f,
                                    -1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        break;
    case eWander:
        // Wander radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "Wander radius",
                                    &m_fWanderRadius,
                                    eDecrease,
                                    0.1f,
                                    0.4f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;
        
        // Wander Distance
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Wander Distance",
                                    &m_fWanderDistance,
                                    eDecrease,
                                    0.1f,
                                    0.5f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        
        // Wander Jitter
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::U, 
                                    "Wander Jitter",
                                    &m_fWanderJitter,
                                    eDecrease,
                                    1.0f,
                                    1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::I );
        ++iCurrentValue;
        break;
    case eInterpose:
        break;
    case ePathFollowing:
        // Dist to waypoint
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "Dist to waypoint",
                                    &m_fDistToWaypoint,
                                    eDecrease,
                                    1.0f,
                                    1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;
        
        // Loop type
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Loop type",
                                    &m_iLoopType,
                                    eDecrease,
                                    &Test_SteeringGameImpl::GetLoopTypeName,
                                    this,
                                    1,
                                    0,
                                    true,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterInt( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y,
                                    smeg::ai::PointPath::eMAX_LOOP_TYPE - 1 );
        ++iCurrentValue;
        
        // Toggle reverse
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::U, 
                                    "Toggle reverse",
                                    &m_bReversePath );
        ++iCurrentValue;
        break;
    case eSeparation:
    case eAlignment:
    case eCohesion:
        // View Radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "View Radius",
                                    &m_fViewRadius,
                                    eDecrease,
                                    1.0f,
                                    10.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;
        break;
    case eFlocking:
        // View Radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "View Radius",
                                    &m_fViewRadius,
                                    eDecrease,
                                    1.0f,
                                    10.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;
        
        // Separation Weight
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Separation Weight",
                                    &m_fSeparationWeight,
                                    eDecrease,
                                    10.0f,
                                    10.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        
        // Alignment Weight
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::U, 
                                    "Alignment Weight",
                                    &m_fAlignmentWeight,
                                    eDecrease,
                                    0.5f,
                                    0.5f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::I );
        ++iCurrentValue;
        
        // Cohesion Weight
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::O, 
                                    "Cohesion Weight",
                                    &m_fCohesionWeight,
                                    eDecrease,
                                    0.5f,
                                    0.5f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::P );
        ++iCurrentValue;
        break;
    case eWallAvoidance:
        // View Radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "View Radius",
                                    &m_fViewRadius,
                                    eDecrease,
                                    1.0f,
                                    10.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;

        // Feeler Length
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Feeler Length",
                                    &m_fFeelerLength,
                                    eDecrease,
                                    1.0f,
                                    1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        
        // Wander radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::Q, 
                                    "Wander radius",
                                    &m_fWanderRadius,
                                    eDecrease,
                                    0.1f,
                                    0.4f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::S );
        ++iCurrentValue;
        
        // Wander Distance
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::D, 
                                    "Wander Distance",
                                    &m_fWanderDistance,
                                    eDecrease,
                                    0.1f,
                                    0.5f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::F );
        ++iCurrentValue;
        
        // Wander Jitter
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::G, 
                                    "Wander Jitter",
                                    &m_fWanderJitter,
                                    eDecrease,
                                    1.0f,
                                    1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::H );
        ++iCurrentValue;
        break;
    case eObstacleAvoidance:
        // View Radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::E, 
                                    "View Radius",
                                    &m_fViewRadius,
                                    eDecrease,
                                    1.0f,
                                    10.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::R );
        ++iCurrentValue;

        // Braking Weight
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::T, 
                                    "Braking Weight",
                                    &m_fBrakingWeight,
                                    eDecrease,
                                    0.1f,
                                    0.2f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::Y );
        ++iCurrentValue;
        
        // Wander radius
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::Q, 
                                    "Wander radius",
                                    &m_fWanderRadius,
                                    eDecrease,
                                    0.1f,
                                    0.4f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::S );
        ++iCurrentValue;
        
        // Wander Distance
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::D, 
                                    "Wander Distance",
                                    &m_fWanderDistance,
                                    eDecrease,
                                    0.1f,
                                    0.5f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::F );
        ++iCurrentValue;
        
        // Wander Jitter
        m_ValuesToChange[iCurrentValue].Setup( sf::Keyboard::G, 
                                    "Wander Jitter",
                                    &m_fWanderJitter,
                                    eDecrease,
                                    1.0f,
                                    1.0f,
                                    false,
                                    &m_ValuesToChange[iCurrentValue + 1] );
        ++iCurrentValue;
        m_ValuesToChange[iCurrentValue].SetupSisterFloat( &m_ValuesToChange[iCurrentValue - 1],
                                    sf::Keyboard::H );
        ++iCurrentValue;
        break;
    }
}

std::ostream& operator<<( std::ostream& o, const Test_SteeringGameImpl::ValueChange& _ValueToDisplay ) {
    if ( _ValueToDisplay.m_bNeedDisplay && _ValueToDisplay.IsValid() ) {
        if ( _ValueToDisplay.m_pSisterValue ) {
            _ValueToDisplay.m_pSisterValue->m_bNeedDisplay = false;
            o << _ValueToDisplay.m_Name << " (" << Test_SteeringGameImpl::GetKeyCodeName(_ValueToDisplay.m_Key) << "/" << Test_SteeringGameImpl::GetKeyCodeName(_ValueToDisplay.m_pSisterValue->m_Key) << ") : ";
        } else {
            o << _ValueToDisplay.m_Name << " (" << Test_SteeringGameImpl::GetKeyCodeName(_ValueToDisplay.m_Key) << ") : ";
        }

        if ( _ValueToDisplay.m_pDisplayValueFunc && _ValueToDisplay.m_pInstance ) {
            o << (_ValueToDisplay.m_pInstance->*_ValueToDisplay.m_pDisplayValueFunc)() << std::endl;
        } else if ( _ValueToDisplay.m_pIntVal ) {
            o << *_ValueToDisplay.m_pIntVal << std::endl;
        } else if ( _ValueToDisplay.m_pFloatVal ) {
            o << *_ValueToDisplay.m_pFloatVal << std::endl;
        } else if ( _ValueToDisplay.m_pBoolVal ) {
            o << (*_ValueToDisplay.m_pBoolVal ? "ON" : "OFF") << std::endl;
        } else {
            o << "No valid value !!!" << std::endl;
        }
    }

    return o;
}

// Value Change
Test_SteeringGameImpl::ValueChange::ValueChange() : 
    m_Type( eDecrease ),
    m_Key( sf::Keyboard::A ),
    m_Name( "No Name" ),
    m_iThreshold( INT_MIN ),
    m_fThreshold( -FLT_MAX ),
    m_bLoop( false ),
    m_pIntVal( NULL ),
    m_pFloatVal( NULL ),
    m_pBoolVal( NULL ),
    m_iAmount( 1 ),
    m_fAmount( 1.0f ),
    m_pDisplayValueFunc( NULL ),
    m_pInstance( NULL ),
    m_pSisterValue( NULL ),
    m_bNeedDisplay( true )
{}

void Test_SteeringGameImpl::ValueChange::Reset() {
    m_Type              = eDecrease;
    m_Key               = sf::Keyboard::A;
    m_Name              = "No Name";

    m_fThreshold        = -FLT_MAX;
    m_iThreshold        = INT_MIN;
    m_bLoop             = false;

    m_pIntVal           = NULL;
    m_pFloatVal         = NULL;
    m_pBoolVal          = NULL;
            
    m_iAmount           = 1;
    m_fAmount           = 1.0f;
            
    m_pDisplayValueFunc = NULL;
    m_pInstance         = NULL;

    m_pSisterValue      = NULL;
    m_bNeedDisplay      = true;
}
        
void Test_SteeringGameImpl::ValueChange::Setup( sf::Keyboard::Key   _Key,
                                                const std::string&  _Name,
                                                int                 *_pIntVal,
                                                eValueChangeType    _Type,
                                                DisplayValueFunc    _pDisplayValueFunc,
                                                Test_SteeringGameImpl* _pInstance,
                                                int                 _iAmount,
                                                int                 _iThreshold,
                                                bool                _bLoop,
                                                ValueChange         *_pSisterValue ) {
    Reset();

    m_Type              = _Type;
    m_Key               = _Key;
    m_Name              = _Name;

    if ( m_Type == eIncrease && _iThreshold == INT_MIN ) {
        m_iThreshold        = INT_MAX;
    } else {
        m_iThreshold    = _iThreshold;
    }
    m_bLoop             = _bLoop;
            
    m_iAmount           = _iAmount;

    m_pIntVal           = _pIntVal;

    m_pDisplayValueFunc = _pDisplayValueFunc;
    m_pInstance         = _pInstance;

    m_pSisterValue      = _pSisterValue;
}

void Test_SteeringGameImpl::ValueChange::Setup( sf::Keyboard::Key   _Key,
                                                const std::string&  _Name,
                                                float               *_pFloatVal,
                                                eValueChangeType    _Type,
                                                float               _fAmount,
                                                float               _fThreshold,
                                                bool                _bLoop,
                                                ValueChange         *_pSisterValue ) {
    Reset();

    m_Type              = _Type;
    m_Key               = _Key;
    m_Name              = _Name;

    if ( m_Type == eIncrease && _fThreshold == -FLT_MAX ) {
        m_fThreshold    = FLT_MAX;
    } else {
        m_fThreshold    = _fThreshold;
    }
    m_bLoop             = _bLoop;
            
    m_fAmount           = _fAmount;

    m_pFloatVal         = _pFloatVal;

    m_pSisterValue      = _pSisterValue;
}

void Test_SteeringGameImpl::ValueChange::Setup( sf::Keyboard::Key   _Key,
                                                const std::string&  _Name,
                                                bool                *_pBoolVal ) {
    Reset();

    m_Key               = _Key;
    m_Name              = _Name;
    m_pBoolVal          = _pBoolVal;
}

void Test_SteeringGameImpl::ValueChange::SetupSisterFloat(  ValueChange*     _pSister,
                                                            sf::Keyboard::Key   _Key,
                                                            float               _fThreshold ) {
    Setup( _Key, 
            _pSister->m_Name,
            _pSister->m_pFloatVal,
            _pSister->m_Type == eDecrease ? eIncrease : eDecrease,
            _pSister->m_fAmount,
            _fThreshold,
            _pSister->m_bLoop,
            _pSister );
    _pSister->m_pSisterValue = this;
}
        
void Test_SteeringGameImpl::ValueChange::SetupSisterInt(    ValueChange*       _pSister,
                                                            sf::Keyboard::Key   _Key,
                                                            int                 _iThreshold ) {
    Setup( m_Key , 
            _pSister->m_Name,
            _pSister->m_pIntVal,
            _pSister->m_Type == eDecrease ? eIncrease : eDecrease,
            _pSister->m_pDisplayValueFunc,
            _pSister->m_pInstance,
            _pSister->m_iAmount,
            _iThreshold,
            _pSister->m_bLoop,
            _pSister );
    _pSister->m_pSisterValue = this;
}

bool Test_SteeringGameImpl::ValueChange::IsValid() const {
    return m_pIntVal || m_pFloatVal || m_pBoolVal;
}

bool Test_SteeringGameImpl::ValueChange::Compute( sf::Keyboard::Key _Code ) {
    if ( IsValid() && m_Key == _Code ) {
        if ( m_pBoolVal ) {
            (*m_pBoolVal) = !(*m_pBoolVal);
        } else {
            if ( eDecrease == m_Type ) {
                if ( m_pFloatVal ) {
                    (*m_pFloatVal) -= m_fAmount;
                    if ( *m_pFloatVal < m_fThreshold ) {
                        if ( m_bLoop && m_pSisterValue ) {
                            *m_pFloatVal = m_pSisterValue->m_fThreshold;
                        } else {
                            *m_pFloatVal = m_fThreshold;
                        }
                    }
                } else if ( m_pIntVal ) {
                    (*m_pIntVal) -= m_iAmount;
                    if ( *m_pIntVal < m_iThreshold ) {
                        if ( m_bLoop && m_pSisterValue ) {
                            *m_pIntVal = m_pSisterValue->m_iThreshold;
                        } else {
                            *m_pIntVal = m_iThreshold;
                        }
                    }
                }
            } else {
                if ( m_pFloatVal ) {
                    (*m_pFloatVal) += m_fAmount;
                    if ( *m_pFloatVal > m_fThreshold ) {
                        if ( m_bLoop && m_pSisterValue ) {
                            *m_pFloatVal = m_pSisterValue->m_fThreshold;
                        } else {
                            *m_pFloatVal = m_fThreshold;
                        }
                    }
                } else if ( m_pIntVal ) {
                    (*m_pIntVal) += m_iAmount;
                    if ( *m_pIntVal > m_iThreshold ) {
                        if ( m_bLoop && m_pSisterValue ) {
                            *m_pIntVal = m_pSisterValue->m_iThreshold;
                        } else {
                            *m_pIntVal = m_iThreshold;
                        }
                    }
                }
            }
        }

        return true;
    }

    return false;
}